package com.ruoyi.common.utils.bean;

import com.ruoyi.common.exception.InteriorException;
import com.ruoyi.common.utils.StringUtils;
import org.apache.poi.ss.formula.functions.T;

import javax.validation.constraints.NotNull;
import java.beans.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * Bean 工具类
 * 
 * @author ruoyi
 */
public class BeanUtils extends org.springframework.beans.BeanUtils
{
    /** Bean方法名中属性名开始的下标 */
    private static final int BEAN_METHOD_PROP_INDEX = 3;

    /** * 匹配getter方法的正则表达式 */
    private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)");

    /** * 匹配setter方法的正则表达式 */
    private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)");

    /**
     * 获取 clazz 中的指定属性
     * 如果不存在返回null
     * 不去检测父类是否存在此属性
     * @param clazz
     * @param fieldName
     * @return
     */
    public static Field getField(@NotNull Class<?> clazz,@NotNull String fieldName){
        for(Field field : clazz.getDeclaredFields()){
            if(field.getName().equals(fieldName)){
                return field;
            }
        }
        return null;
    }

    /**
     * 获取当前 clazz 类 下第一个带有  annotation 类型 的属性并返回
     * @param clazz
     * @param annotation
     * @return
     */
    public static Field getFieldByAnnotation(@NotNull Class<?> clazz,@NotNull Class<? extends Annotation> annotation){
        for(Field field : clazz.getDeclaredFields()){
            if(StringUtils.isNotNull(field.getAnnotation(annotation))){
                return field;
            }
        }
        return null;
    }
    /**
     * 获取 class 中指定属性上的注解
     * 如果不存在该属性 或该属性不存在此注解 返回null
     * 不去检测父类的属性
     * @param clazz 元类
     * @param fieldName 属性名称
     * @param aClass 注解class对象
     * @return
     */
    public static <T extends Annotation> T getAnnotationOnField(@NotNull Class<?> clazz, @NotNull String fieldName, @NotNull Class<T> aClass){
        Field field ;
        T annotation = null;
        if((field = getField(clazz,fieldName))!=null){
            annotation = field.getAnnotation(aClass);
        }
        return annotation;
    }

    public static <T> T initialize(Class<T> clazz){
        return initialize(clazz,null);
    }
    /**
     * 初始化bean
     * @param clazz
     * @param consumer
     * @return
     */
    public static <T> T initialize(Class<T> clazz, Consumer<T> consumer){
        T t = null;
        try {
            t = clazz.newInstance();
            if(consumer!=null){
                consumer.accept(t);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return t;
    }
    /**
     * Bean属性复制工具方法。
     * 
     * @param dest 目标对象
     * @param src 源对象
     */
    public static void copyBeanProp(Object dest, Object src)
    {
        try
        {
            copyProperties(src, dest);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    /**
     * 获取对象的setter方法。
     * 
     * @param obj 对象
     * @return 对象的setter方法列表
     */
    public static List<Method> getSetterMethods(Object obj)
    {
        // setter方法列表
        List<Method> setterMethods = new ArrayList<Method>();

        // 获取所有方法
        Method[] methods = obj.getClass().getMethods();

        // 查找setter方法

        for (Method method : methods)
        {
            Matcher m = SET_PATTERN.matcher(method.getName());
            if (m.matches() && (method.getParameterTypes().length == 1))
            {
                setterMethods.add(method);
            }
        }
        // 返回setter方法列表
        return setterMethods;
    }

    /**
     * 获取对象的getter方法。
     * 
     * @param obj 对象
     * @return 对象的getter方法列表
     */

    public static List<Method> getGetterMethods(Object obj)
    {
        // getter方法列表
        List<Method> getterMethods = new ArrayList<Method>();
        // 获取所有方法
        Method[] methods = obj.getClass().getMethods();
        // 查找getter方法
        for (Method method : methods)
        {
            Matcher m = GET_PATTERN.matcher(method.getName());
            if (m.matches() && (method.getParameterTypes().length == 0))
            {
                getterMethods.add(method);
            }
        }
        // 返回getter方法列表
        return getterMethods;
    }

    /**
     * 获取 clazz 的属性描述Map
     * @param clazz
     * @return Map<K:属性名称,V:PropertyDescriptor>
     */
    public static Map<String, PropertyDescriptor> getPropertyDescriptorMap(Class<?> clazz){

        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
            return Arrays.stream(beanInfo.getPropertyDescriptors()).collect(Collectors.toMap(PropertyDescriptor::getName, v -> v));
        } catch (IntrospectionException e) {

            e.printStackTrace();

            throw new InteriorException(MessageFormat.format("获取 class:{} 对象的属性信息失败！",clazz));
        }
    }

    /**
     * 获取 obj对象 的属性描述Map
     * @param obj
     * @return Map<K:属性名称,V:PropertyDescriptor>
     */
    public static Map<String, PropertyDescriptor> getPropertyDescriptorMap(Object obj){

        return getPropertyDescriptorMap(obj.getClass());
    }

    /**
     * 检查Bean方法名中的属性名是否相等。<br>
     * 如getName()和setName()属性名一样，getName()和setAge()属性名不一样。
     * 
     * @param m1 方法名1
     * @param m2 方法名2
     * @return 属性名一样返回true，否则返回false
     */

    public static boolean isMethodPropEquals(String m1, String m2)
    {
        return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX));
    }

    /**
     * 判断一个对象是否为java对象
     * @param tClass
     * @return true 是java对象 false 不是java对象
     * 为什么getClassLoader()为null是JAVA类型？
     *
     * 这里就要提到JVM的类加载机制：Bootstrap classLoader。
     *
     * Bootstrap classLoader：采用native code实现，是JVM的一部分，主要加载JVM自身工作需要的类，如java.lang.*、java.uti.*等； 这些类位于$JAVA_HOME/jre/lib/rt.jar。Bootstrap ClassLoader不继承自ClassLoader，因为它不是一个普通的Java类，底层由C++编写，已嵌入到了JVM内核当中，当JVM启动后，Bootstrap ClassLoader也随着启动，负责加载完核心类库后，并构造Extension ClassLoader和App ClassLoader类加载器。
     *
     * BootStrap ClassLoader加载rt.jar下的文件，也就是java最最核心的部分；然后由Extension ClassLoader加载ext下的文件；再有App ClassLoader加载用户自己的文件。
     *
     * 由于BootStrap ClassLoader是用c++写的，所以在返回该ClassLoader时会返回null。
     */
    public static boolean isJavaClass(Class tClass){
        return tClass != null && tClass.getClassLoader() == null;
    }
    /**
     * 默认获取第一个
     * @param o
     * @return
     */
    public static Class<?> getClassParameterizedType(Object o){
        return getClassParameterizedType(o,0);
    }
    /**
     * 获取类上的泛型
     * @param o 实例对象
     * @param index 需要获取第几个泛型 类上可以有多个泛型对象
     * @return
     */
    public static Class<?> getClassParameterizedType(Object o,int index){
        Type type = o.getClass().getGenericSuperclass();
        //ParameterizedType是Type的子接口，表示一个有参数的类型，例如Collection<T>，Map<K,V>等。.
        //但实现上 ParameterizedType并不直接表示Collection<T>和Map<K,V>等，而是表示 Collection<String>和Map<String,String>等这种具体的类型。
        if(type instanceof ParameterizedType){
            ParameterizedType parameterizedType = (ParameterizedType)type;
            //得到实际的类型参数
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            return actualTypeArguments[index].getClass();
        }
        String className = type == null ? "null" : type.getClass().getName();
        throw new IllegalArgumentException("Expected a Class, ParameterizedType"
                + ", but <" + type + "> is of type " + className);
    }

    private static Class<?> checkType(Type type,int index){
        if(type instanceof Class<?>){
            return (Class<?>)type;
        }else if(type instanceof ParameterizedType){
            ParameterizedType parameterizedType = (ParameterizedType)type;
            //得到实际的类型参数
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();

            return checkType(actualTypeArguments[index],index);
        }else{
            String className = type == null ? "null" : type.getClass().getName();
            throw new IllegalArgumentException("Expected a Class, ParameterizedType"
                    + ", but <" + type + "> is of type " + className);
        }
    }
}
